package helpers;

import interfaces.State;
import interfaces.Transition;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import exceptions.NotDeterministInitalStateException;
import exceptions.NotDeterministTransitionException;
import exceptions.UnknownInitialStateException;


import automaton.DFA;
import automaton.TransitionImpl;

public class AutomataOperations {
	/**
	 * Get all labels on transitions in automaton
	 * @param automata
	 * @return The set of alphabets
	 */
	public static HashSet<String> getLabels(DFA automata){
		HashSet<String> listLabels = new HashSet<String>();				
		for(Map.Entry<State,Map<String,Transition<String>>> entry: automata.getDFA().entrySet()){		
			listLabels.addAll(entry.getValue().keySet());								
		}
		return listLabels;
	}
	
	/**
	 * Get all states of automata
	 * @param automata
	 * @return
	 */
	public static HandleHashSet getAllStates(DFA automata){
		HandleHashSet result = new HandleHashSet();
		for(Map.Entry<State,Map<String,Transition<String>>> entry: automata.getDFA().entrySet()){						
			result.add(entry.getKey());
		}
		return result;
	}
	
	/**
	 * Get all states reach to set states inside with label (a^(-1)P)
	 * @param automata
	 * @param label
	 * @param inside
	 * @return
	 */
	public static HandleHashSet getSplitStates(DFA automata, String label, HandleHashSet inside){
		HandleHashSet setStates = new HandleHashSet();		
		for (State state : inside) {
			setStates.addAll(automata.getSet_StateSource(state, label));
		}		
		return setStates;
	}
	
	private static HandleHashSet getSetByState(HashSet<HandleHashSet> partitions ,State stt){
		for (HandleHashSet hset : partitions) {
			if(HandleHashSet.checkStateIntoSet(hset, stt))
				return hset;
		}
		return null;
	}
	
@SuppressWarnings("unchecked")
public static DFA minimizeAutomata(DFA automata, HashSet<HandleHashSet> partitions) 
		throws UnknownInitialStateException, NotDeterministInitalStateException, 
		NotDeterministTransitionException{
	Map<State, State> mapStates = new HashMap<State, State>();
	for(Map.Entry<State,Map<String,Transition<String>>> entry: automata.getDFA().entrySet()){		
		mapStates.put(entry.getKey(), HandleHashSet.composedState(getSetByState(partitions, entry.getKey())));					
	}
	
	HashSet<Transition<String>> listT = new HashSet<>();
	Iterator<HandleHashSet> listPart = partitions.iterator();
	while(listPart.hasNext()){
		HandleHashSet hashset = listPart.next();		
		for (State state : hashset) {
			HandleHashSet sameSet = HandleHashSet.getSameSet(hashset, state);// get member same set with state
			
			List<Transition<String>> t1 = new ArrayList<Transition<String>>(automata.getDFA().get(state).values());
			for (Transition<String> tran : t1) {
				State source = tran.source();
				State target = tran.target();
				if(source !=null && target !=null){
					if(!(HandleHashSet.checkStateIntoSet(sameSet, source)
							&& HandleHashSet.checkStateIntoSet(sameSet, target))
							|| source.equals(target) ){
						
						TransitionImpl<String> tempT = 
								new TransitionImpl<String>(mapStates.get(source), mapStates.get(target), tran.label());
								
						if(!checkInsideTrans(listT,tempT))
							listT.add(tempT);
					}				
				}
			}
		}
	}
		
	Transition<String>[] result = new TransitionImpl[listT.size()];
	listT.toArray(result);
	DFA dfamini = new DFA(result);
	
	/*State[] st = new StateImpl[dfamini.getDFA().keySet().size()];
	dfamini.getDFA().keySet().toArray(st);
	for(int i=0;i<st.length;i++){
		((StateImpl)st[i]).setLabel(String.valueOf(i));
	}*/
	
		return dfamini;
	}

	/**
	 * Check a transition exist in a set of transitions or not
	 * @param set
	 * @param trans
	 * @return
	 */
	private static boolean checkInsideTrans(HashSet<Transition<String>> set, TransitionImpl<String> trans ){
		Iterator<Transition<String>> iterator = set.iterator();
		while (iterator.hasNext()) {
			TransitionImpl<String> tran = (TransitionImpl<String>)iterator.next();
			if(tran.equals(trans))
				return true;			
		}
		return false;
	}
}
